import { RedisModule, RedisService } from '@liaoliaots/nestjs-redis';
import { BullModule } from '@nestjs/bull';
import { ClassSerializerInterceptor, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { ThrottlerModule, seconds } from '@nestjs/throttler';
import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis';
import { configs } from './configs';
import constants from './constants';
import { controllers } from './controllers';
import { cores } from './core';
import { AllExceptionsFilter } from './filters';
import { JwtAuthGuard, ThrottlerBehindProxyGuard } from './guards';
import { processors } from './processors';
import { services } from './services';
import { strategies } from './strategies';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({ secret: constants.jwt.secret }),
    ScheduleModule.forRoot(),
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      envFilePath: [`env/${process.env.NODE_ENV}.env`],
      load: configs,
    }),
    RedisModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        config: {
          ...config.get('redis'),
          keyPrefix: config.get('redis.keyPrefix') + ':',
        },
      }),
    }),
    ThrottlerModule.forRootAsync({
      inject: [RedisService],
      useFactory: (redisService: RedisService) => {
        return {
          storage: new ThrottlerStorageRedisService(redisService.getClient()),
          throttlers: [
            {
              name: 'short',
              ttl: seconds(5),
              limit: 200,
            },
            {
              name: 'medium',
              ttl: seconds(10),
              limit: 1000,
            },
            {
              name: 'long',
              ttl: seconds(60),
              limit: 6000,
            },
          ],
        };
      },
    }),
    BullModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        prefix: `${config.get('redis.keyPrefix')}:bull`,
        redis: {
          host: config.get('redis.host'),
          port: config.get('redis.port'),
          password: config.get('redis.password'),
          db: config.get('redis.db'),
        },
        limiter: {
          max: 5000,
          duration: 10000,
          bounceBack: false,
        },
        defaultJobOptions: {
          removeOnComplete: true,
          removeOnFail: true,
          attempts: 1,
        },
      }),
    }),
    BullModule.registerQueue({
      name: 'chain',
    }),
    BullModule.registerQueue({
      name: 'wallet',
    }),
    BullModule.registerQueue({
      name: 'bill',
    }),
    BullModule.registerQueue({
      name: 'notice',
    }),
    BullModule.registerQueue({
      name: 'order',
    }),
    BullModule.registerQueue({
      name: 'autotrading',
    }),
    BullModule.registerQueue({
      name: 'market',
    }),
    BullModule.registerQueue({
      name: 'autosale',
    }),
  ],
  controllers,
  providers: [
    ...cores,
    ...services,
    ...processors,
    ...strategies,
    {
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    },
    {
      provide: APP_GUARD,
      useClass: ThrottlerBehindProxyGuard,
    },
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: ClassSerializerInterceptor,
    },
  ],
  exports: [JwtModule],
})
export class AppModule {}
